<template>
  <div class="virtuallist-container" @scroll="handleScroll" ref="container">
    <div class="list" :style="scrollStyle">
      <div v-for="(item, index) in renderList" :key="index" class="item" :style="{ lineHeight: itemHeight + 'px' }">
        <slot :item="item"></slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from "vue";

const props = defineProps<{
  list: Array<any>;
  itemHeight: number;
  viewHeight: number;
}>();

const container = ref<HTMLElement | null>(null);
let startIndex = ref(0);
let maxCount = Math.ceil(props.viewHeight / props.itemHeight) + 1;

const endIndex = computed(() => {
  const end = startIndex.value + maxCount;
  return props.list[end] ? end : props.list.length;
});

const renderList = computed(() => {
  return props.list.slice(startIndex.value, endIndex.value);
});

const scrollStyle = computed(() => {
  return {
    height: `${props.list.length * props.itemHeight - props.itemHeight * startIndex.value}px`,
    transform: `translateY(${startIndex.value * props.itemHeight}px)`,
  };
});

const refThrottle = (fn: Function) => {
  let lock = false;
  return function (this: void, ...args: any) {
    window.requestAnimationFrame(() => {
      if (lock) return;
      lock = true;
      fn.apply(this, args);
      lock = false;
    });
  };
};

const handleScroll = refThrottle((e: any) => {
  const scrollTop = e.target.scrollTop;
  startIndex.value = Math.floor(scrollTop / props.itemHeight);
});

// 暴露 scrollTop 方法
defineExpose({
  setScrollTop(scrollTop: number) {
    if (container.value) {
      container.value.scrollTop = scrollTop;
    }
  },
});
</script>

<style scoped>
.virtuallist-container {
  width: 100%;
  height: 100%;
  overflow: auto;
}

.list {
  width: 100%;
}

.item {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  font-size: 14px;
}
</style>
